import React, { useState, useEffect, ReactNode } from 'react';
import { DownOutlined, RightOutlined } from '@ant-design/icons';
import {
  FaDatabase,
  FaFolder,
  FaFolderOpen,
  FaListUl,
  FaTable,
} from 'react-icons/fa';
import { Key } from 'antd/lib/table/interface';
import { Link } from 'react-router';
import './custom.css';
import styles from '../../Common/Layout/LeftSubSidebar/LeftSubSidebar.module.scss';
import {
  getFunctionModifyRoute,
  getTableBrowseRoute,
} from '../../Common/utils/routesUtils';
import GqlCompatibilityWarning from '../../Common/GqlCompatibilityWarning/GqlCompatibilityWarning';
import { GDCTree } from './GDCTree/GDCTree';
import { useTreeData } from './GDCTree';

type SourceItemsTypes =
  | 'database'
  | 'schema'
  | 'view'
  | 'enum'
  | 'function'
  | 'table';

const sourceItemsTypesToIcons: Record<SourceItemsTypes, ReactNode> = {
  enum: <FaListUl />,
  database: <FaDatabase />,
  function: <FaDatabase />,
  schema: <></>,
  table: <FaTable />,
  view: <></>,
};

type SourceItem = {
  name: string;
  type: SourceItemsTypes;
  children?: SourceItem[];
};

const activeStyle = {
  color: '#fd9540',
};

const legacyIconStyles = 'ml-2 mr-[15px]';

const filterItemsBySearch = (searchQuery: string, itemList: SourceItem[]) => {
  const caseSensitiveResults: SourceItem[] = [];
  const caseAgnosticResults: SourceItem[] = [];
  itemList.forEach(item => {
    if (item.name.includes(searchQuery)) {
      caseSensitiveResults.push(item);
    } else if (item.name.toLowerCase().includes(searchQuery.toLowerCase())) {
      caseAgnosticResults.push(item);
    }
  });

  return [
    ...caseSensitiveResults.sort((item1, item2) => {
      return item1.name.search(searchQuery) > item2.name.search(searchQuery)
        ? 1
        : -1;
    }),
    ...caseAgnosticResults.sort((item1, item2) => {
      return item1.name.toLowerCase().search(searchQuery.toLowerCase()) >
        item2.name.toLowerCase().search(searchQuery.toLowerCase())
        ? 1
        : -1;
    }),
  ];
};

type LeafItemsViewProps = {
  item: SourceItem;
  currentSource: string;
  currentSchema: string;
  pathname: string;
};
const LeafItemsView: React.FC<LeafItemsViewProps> = ({
  item,
  currentSource,
  currentSchema,
  pathname,
}) => {
  const [isOpen, setIsOpen] = useState(false);
  const regex = new RegExp(
    `\\/data\\/${currentSource}\\/schema\\/${currentSchema}\\/(tables|functions|views)\\/${item.name}\\/`
  );
  const isActive = regex.test(pathname);

  const isView = item.type === 'view';

  const iconStyle = {
    marginRight: '5px',
    fontSize: '12px',
    width: '12px',
  };

  const activeIcon =
    '';
  const nonActiveIcon =
    '';

  return (
    <div
      className={styles.sidebarTablePadding}
      onClick={() => {
        setIsOpen(prev => !prev);
      }}
      onKeyDown={() => {
        setIsOpen(prev => !prev);
      }}
      role="button"
    >
      <span
        className={
          item.children && `${styles.title} ${isOpen ? '' : styles.titleClosed}`
        }
      >
        {item.type === 'function' ? (
          <Link
            to={getFunctionModifyRoute(currentSchema, currentSource, item.name)}
            data-test={item.name}
            style={isActive ? activeStyle : {}}
          >
            <img
              style={iconStyle}
              src={isActive ? activeIcon : nonActiveIcon}
              alt="function icon"
            />
            {item.name}
          </Link>
        ) : (
          <>
            <Link
              to={getTableBrowseRoute(
                currentSchema,
                currentSource,
                item.name,
                !isView
              )}
              data-test={item.name}
              style={isActive ? activeStyle : {}}
            >
              {item.type === 'enum' ? (
                <FaListUl className="mr-1" />
              ) : (
                <FaTable className="mr-1" />
              )}
              {item.type === 'view' ? <i>{item.name}</i> : item.name}
            </Link>
            <GqlCompatibilityWarning
              identifier={item.name}
              className={styles.add_mar_left_mid}
              ifWarningCanBeFixed
            />
          </>
        )}
      </span>
    </div>
  );
};

type SchemaItemsViewProps = {
  item: SourceItem;
  currentSource: string;
  isActive: boolean;
  setActiveSchema: (value: string) => void;
  pathname: string;
  databaseLoading: boolean;
  schemaLoading: boolean;
};
const SchemaItemsView: React.FC<SchemaItemsViewProps> = ({
  item,
  currentSource,
  isActive,
  setActiveSchema,
  pathname,
  databaseLoading,
  schemaLoading,
}) => {
  const [isOpen, setIsOpen] = useState(false);

  const [search, setSearch] = React.useState('');
  const onSearchChange = React.useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => {
      setSearch(e.target.value);
    },
    [search]
  );

  const showActiveStyle =
    pathname === `/data/${currentSource}/schema/${item.name}`;
  useEffect(() => {
    setIsOpen(isActive);
  }, [isActive]);

  const itemSearchResults = search
    ? filterItemsBySearch(search, item.children || [])
    : item.children;

  return (
    <>
      <div
        onClick={() => {
          setActiveSchema(encodeURIComponent(item.name));
        }}
        onKeyDown={() => {
          setActiveSchema(encodeURIComponent(item.name));
        }}
        role="button"
        className={styles.padd_bottom_small}
        style={showActiveStyle ? activeStyle : {}}
      >
        <span
          className={
            item.children &&
            `${styles.title} ${isOpen ? '' : styles.titleClosed}`
          }
        >
          {isOpen ? (
            <DownOutlined className={`text-xs ${legacyIconStyles}`} />
          ) : (
            <RightOutlined className={`text-xs ${legacyIconStyles}`} />
          )}
          {isOpen ? <FaFolderOpen /> : <FaFolder />} {item.name}
        </span>
      </div>
      {isOpen && itemSearchResults ? (
        !(databaseLoading || schemaLoading) ? (
          item.children?.length ? (
            <>
              <div className="my-1 px-sm">
                <input
                  type="text"
                  onChange={onSearchChange}
                  className="form-control"
                  placeholder={`Search tables in ${item.name}....`}
                  data-test="search-tables"
                />
              </div>
              <ul className={styles.reducedChildPadding}>
                {itemSearchResults.map((child, key) => (
                  <li key={key}>
                    <LeafItemsView
                      item={child}
                      currentSource={currentSource}
                      currentSchema={item.name}
                      key={key}
                      pathname={pathname}
                    />
                  </li>
                ))}
              </ul>
            </>
          ) : (
            <li
              className="font-normal px-sm"
              data-test="table-sidebar-no-tables"
            >
              <i>No tables or views in this schema</i>
            </li>
          )
        ) : (
          <span
            className={`${styles.sidebarTablePadding} ${styles.padd_bottom_small}`}
          >
            <FaTable />
            <span className={styles.loaderBar} />
          </span>
        )
      ) : null}
    </>
  );
};

type DatabaseItemsViewProps = {
  item: SourceItem;
  isActive: boolean;
  setActiveDataSource: (activeSource: string) => void;
  onSchemaChange: (value: string) => void;
  currentSchema: string;
  pathname: string;
  databaseLoading: boolean;
  schemaLoading: boolean;
};
const DatabaseItemsView: React.FC<DatabaseItemsViewProps> = ({
  item,
  isActive,
  setActiveDataSource,
  onSchemaChange,
  currentSchema,
  pathname,
  databaseLoading,
  schemaLoading,
}) => {
  const [isOpen, setIsOpen] = useState(false);
  const showActiveStyle = [
    `/data/${item.name}/`,
    `/data/${item.name}`,
    `/data/${item.name}/display`,
    `/data/${item.name}/gallery`,
  ].includes(pathname);

  useEffect(() => {
    setIsOpen(isActive);
  }, [isActive]);
  const handleSelectSchema = (value: string) => {
    onSchemaChange(value);
  };

  return (
    <div className={styles.padd_bottom_small}>
      <div
        onClick={() => {
          setActiveDataSource(item.name);
        }}
        onKeyDown={() => {
          setActiveDataSource(item.name);
        }}
        role="button"
        className={styles.padd_bottom_small + ' !text-gray-500'}
      >
        <span
          className={
            item.children &&
            `${styles.title} ${isOpen ? '' : styles.titleClosed}`
          }
          style={showActiveStyle ? activeStyle : {}}
        >
          {isOpen ? (
            <DownOutlined className={`text-xs ${legacyIconStyles}`} />
          ) : (
            <RightOutlined className={`text-xs ${legacyIconStyles}`} />
          )}
          {sourceItemsTypesToIcons[item.type]} {item.name}
        </span>
      </div>
      {isOpen && item.children
        ? item.children.map((child, key) => (
            <li key={key}>
              <SchemaItemsView
                item={child}
                currentSource={item.name}
                isActive={child.name === currentSchema}
                setActiveSchema={handleSelectSchema}
                key={key}
                pathname={pathname}
                databaseLoading={databaseLoading}
                schemaLoading={schemaLoading}
              />
            </li>
          ))
        : null}
      {databaseLoading && isActive ? (
        <li>
          <span
            className={`${styles.title} ${styles.titleClosed} ${styles.padd_bottom_small}`}
          >
            <FaFolder />
            <span className={styles.loaderBar} />
          </span>
        </li>
      ) : null}
    </div>
  );
};

type TreeViewProps = {
  items: SourceItem[];
  onDatabaseChange: (value: string) => void;
  onSchemaChange: (value: string) => void;
  currentDataSource: string;
  currentSchema: string;
  pathname: string;
  databaseLoading: boolean;
  schemaLoading: boolean;
  preLoadState: boolean;
  gdcItemClick: (value: Key[]) => void;
};
const TreeView: React.FC<TreeViewProps> = ({
  items,
  onDatabaseChange,
  currentDataSource,
  onSchemaChange,
  currentSchema,
  pathname,
  databaseLoading,
  schemaLoading,
  preLoadState,
  gdcItemClick,
}) => {
  const handleSelectDataSource = (dataSource: string) => {
    onDatabaseChange(dataSource);
  };

  const { data: gdcDatabases } = useTreeData();

  const allDatabases = [...items, ...(gdcDatabases ?? [])];

  if (allDatabases.length === 0) {
    return preLoadState ? (
      <div className={styles.treeNav}>
        <span className={`${styles.title} ${styles.padd_bottom_small}`}>
          <FaDatabase />
        </span>
        <span className={styles.loaderBar} />
        <li>
          <span className={`${styles.title} ${styles.padd_bottom_small}`}>
            <FaFolder />
            <span className={styles.loaderBar} />
            <ul className={styles.reducedChildPadding}>
              <li
                className={`${styles.sidebarTablePadding} ${styles.add_mar_left_mid}`}
              >
                <FaTable />
                <span className={styles.loaderBar} />
              </li>
            </ul>
          </span>
        </li>
      </div>
    ) : (
      <li className={styles.noChildren} data-test="sidebar-no-services">
        <i>No data available</i>
      </li>
    );
  }

  return (
    <div className={styles.treeNav}>
      {items.map((item, key) => (
        <DatabaseItemsView
          item={item}
          onSchemaChange={onSchemaChange}
          key={key}
          isActive={currentDataSource === item.name}
          setActiveDataSource={handleSelectDataSource}
          currentSchema={currentSchema}
          pathname={pathname}
          databaseLoading={databaseLoading}
          schemaLoading={schemaLoading}
        />
      ))}
      <div id="tree-container" className="inline-block">
        <GDCTree onSelect={gdcItemClick} treeData={gdcDatabases ?? []} />
      </div>
    </div>
  );
};

export default TreeView;
